package com.spzx.product.service.impl;

import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.spzx.common.redis.cache.GuiguCache;
import com.spzx.product.api.domain.*;
import com.spzx.product.domain.SkuStock;
import com.spzx.product.mapper.*;
import com.spzx.product.service.IProductService;
import com.spzx.product.util.TUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @Author ChenXing
 * @Description
 * @Create 2024/7/29 11:02
 */
@Slf4j
@Service

public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements IProductService {

    @Autowired
    private ProductMapper productMapper;

    @Autowired
    SkuStockMapper skuStockMapper;

    @Autowired
    ProductDetailsMapper productDetailsMapper;

    @Autowired
    ProductSkuMapper productSkuMapper;

    @Autowired
    private ProductSpecMapper productSpecMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public List<Product> selectProductList(Product product) {
        return baseMapper.selectProductList(product);
    }

    // 一旦发生任何异常，事物立刻回滚
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int insertProduct(Product product) {
        // spu
        productMapper.insert(product);

      /*  // 添加 sku 这里要搞清楚，传进来的 product 是什么 包不包括 productSkuList里面的数值
        List<ProductSku> productSkuList = product.getProductSkuList();
        int i = 0;
        for (ProductSku productSku : productSkuList) {
            i++;
            productSku.setProductId(product.getId());// productId
            // skucode
            String skuCode = product.getId() + "_" + i;
            productSku.setSkuCode(skuCode);
            // skuname
            String skuName = product.getName() + " " + productSku.getSkuSpec();
            productSku.setSkuName(skuName);
            productSkuMapper.insert(productSku);

            //stock设置
            SkuStock skuStock = new SkuStock();
            skuStock.setSkuId(productSku.getId());// skuId
            skuStock.setStatus(1);
            skuStock.setSaleNum(0);
            skuStock.setLockNum(0);
            skuStock.setAvailableNum(productSku.getStockNum());// 可用数量
            skuStock.setTotalNum(productSku.getStockNum());
            skuStockMapper.insert(skuStock);
        }
            // details
            ProductDetails productDetails = new ProductDetails();


            productDetails.setProductId(product.getId());
            List<String> detailsImageUrlList = product.getDetailsImageUrlList();

            // 将传过来的数据转化为字符串
            String join = String.join(",", detailsImageUrlList);
//        String imgs = "";
//        for (String str : detailsImageUrlList) {
//            imgs = imgs+"," +str;
//        }
            productDetails.setImageUrls(join);// 字符串
            productDetailsMapper.insert(productDetails);


            // 这是Long类的一个方法，用于将Long类型的值转换为基本数据类型int
            return product.getId().intValue();*/


        List<ProductSku> productSkuList = product.getProductSkuList();
        for (int i = 0, size = productSkuList.size(); i < size; i++) {
            ProductSku productSku = productSkuList.get(i);
            productSku.setSkuCode(product.getId() + "_" + i);
            productSku.setProductId(product.getId());
            String skuName = product.getName() + " " + productSku.getSkuSpec();
            productSku.setSkuName(skuName);
            productSkuMapper.insert(productSku);

            //添加商品库存
            SkuStock skuStock = new SkuStock();
            skuStock.setSkuId(productSku.getId());
            skuStock.setTotalNum(productSku.getStockNum());
            skuStock.setLockNum(0);
            skuStock.setAvailableNum(productSku.getStockNum());
            skuStock.setSaleNum(0);
            skuStockMapper.insert(skuStock);
        }

        ProductDetails productDetails = new ProductDetails();
        productDetails.setProductId(product.getId());
        productDetails.setImageUrls(String.join(",", product.getDetailsImageUrlList()));
        productDetailsMapper.insert(productDetails);

        return product.getId().intValue();
    }

    @Override
    public Product selectProductById(Long id) {
        Product product = baseMapper.selectProductById(id);

        // 海报(商品详情)
        ProductDetails productDetails = productDetailsMapper.selectOne(new LambdaQueryWrapper<ProductDetails>().eq(ProductDetails::getProductId, id));

//        调用 productDetails.getImageUrls()：这会返回一个字符串，通常是逗号分隔的图片 URL 列表。
//        使用 split(",") 方法：将上述字符串按逗号分隔，生成一个字符串数组。
//        使用 Arrays.asList()：将字符串数组转换为一个 List<String>。

        List<String> productDetailsList = Arrays.asList(productDetails.getImageUrls().split(","));


        product.setDetailsImageUrlList(productDetailsList);

        // sku
        List<ProductSku> productSkus = productSkuMapper.selectList(new LambdaQueryWrapper<ProductSku>().eq(ProductSku::getProductId, id));

//      以下方法要调用 n 次查询，性能太差
//        for (ProductSku sku : productSkus) {
//            Long skuId = sku.getId();
//            SkuStock skuStock = skuStockMapper.selectOne(new LambdaQueryWrapper<SkuStock>().eq(SkuStock::getSkuId, skuId));
//            sku.setStockNum(skuStock.getTotalNum());
//        }

        List<Long> skuIds = productSkus.stream().map(sku -> {
            return sku.getId();
        }).collect(Collectors.toList());

        // 使用 .in() 方法添加查询条件，指定 SkuStock 实体的 getSkuId 属性的值应该包含在 skuIds 集合中。
        List<SkuStock> skuStocks = skuStockMapper.selectList(new LambdaQueryWrapper<SkuStock>().in(SkuStock::getSkuId, skuIds));


        for (ProductSku sku : productSkus) {
            for (SkuStock skuStock : skuStocks) {
                if (sku.getId().longValue() == skuStock.getSkuId().longValue()) {
                    sku.setStockNum(skuStock.getTotalNum());
                }
            }
        }

//        Map<Long, Integer> skuStockMap = skuStocks.stream().collect(Collectors.toMap(SkuStock::getSkuId, skuStock -> skuStock.getTotalNum()));
//        System.out.println(skuStockMap);
//        for (ProductSku sku : productSkus) {
//            sku.setStockNum(skuStockMap.get(sku.getId()));
//        }
        product.setProductSkuList(productSkus);
        return product;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public int updateProduct(Product product) {
        //修改商品信息
        productMapper.updateById(product);

        List<ProductSku> productSkuList = product.getProductSkuList();

        // 还可以这样循环遍历，和流的效果差不多
        productSkuList.forEach(productSku -> {
            //修改商品SKU信息
            productSkuMapper.updateById(productSku);

            //修改商品库存
            SkuStock skuStock = skuStockMapper.selectOne(new LambdaQueryWrapper<SkuStock>().eq(SkuStock::getSkuId, productSku.getId()));
            skuStock.setTotalNum(productSku.getStockNum());
            int availableNum = skuStock.getTotalNum() - skuStock.getLockNum();
            skuStock.setAvailableNum(availableNum);
            skuStockMapper.updateById(skuStock);
        });

        //修改商品详细信息
        ProductDetails productDetails = productDetailsMapper.selectOne(new LambdaQueryWrapper<ProductDetails>().eq(ProductDetails::getProductId, product.getId()));
        productDetails.setImageUrls(String.join(",", product.getDetailsImageUrlList()));
        productDetailsMapper.updateById(productDetails);
        return 1;

    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public int deleteProductByIds(Long[] ids) {
        productMapper.deleteBatchIds(Arrays.asList(ids));
        //获取sku列表
        List<ProductSku> productSkuList = productSkuMapper.selectList(new LambdaQueryWrapper<ProductSku>().in(ProductSku::getProductId, ids).select(ProductSku::getId));
        List<Long> skuIdList = productSkuList.stream().map(ProductSku::getId).collect(Collectors.toList());
        productSkuMapper.delete(new LambdaQueryWrapper<ProductSku>().in(ProductSku::getProductId, ids));
        skuStockMapper.delete(new LambdaQueryWrapper<SkuStock>().in(SkuStock::getSkuId, skuIdList));
        productDetailsMapper.delete(new LambdaQueryWrapper<ProductDetails>().in(ProductDetails::getProductId, ids));
        return 1;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateAuditStatus(Long id, Integer auditStatus) {
        Product product = new Product();
        product.setId(id);
        if (auditStatus == 1) {
            product.setAuditStatus(1);
            product.setAuditMessage("审批通过");
        } else {
            product.setAuditStatus(-1);
            product.setAuditMessage("审批不通过");
        }
        productMapper.updateById(product);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateStatus(Long id, Integer status) {
        Product product = new Product();
        product.setId(id);
        if (status == 1) {
            product.setStatus(1);
        } else {
            product.setStatus(-1);
        }
        productMapper.updateById(product);
    }

    @Override
    public List<ProductSku> getTopSale() {

        List<ProductSku> productSkuList = productSkuMapper.selectTopSale();
        return productSkuList;
    }

    @Override
    public List<ProductSku> selectProductSkuList(SkuQuery skuQuery) {
        List<ProductSku> productSkuList = productSkuMapper.selectProductSkuList(skuQuery);
        return productSkuList;
    }

    //    @Override
//    public ProductSku getProductSku(Long skuId) {
//        ProductSku productSku = productSkuMapper.selectById(skuId);
//        return productSku;
//    }
// 对这个代码进行 redis 缓存改造

    @Override
    public ProductSku getProductSku(Long skuId) {

        String key = "product:sku:" + skuId; // 这个是 redis 里面 key value 对应的 key
        ProductSku productSku = null;
        // 先访问缓存
        // redis 里面支持强转
        productSku = (ProductSku) redisTemplate.opsForValue().get(key);

        if (productSku == null) {

            // 尝试获得 分布式锁
            String lockKey = "product:sku:" + skuId + ":lock"; // 这个是 锁 的名字，颗粒度 直接到 skuId
            String dflag = UUID.randomUUID().toString();// 删锁标识位
            Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent(lockKey, dflag);

            if (aBoolean) {

                try {
                    // 缓存没有加载,直接访问mysql
                    productSku = getProductSkuFromDB(skuId);
                    if (productSku != null) {
                        // 同步数据库信息到 redis
                        redisTemplate.opsForValue().set(key, productSku);
                    } else {
                        // 数据库也加载不到，有缓存穿透的嫌疑
                        redisTemplate.opsForValue().set(key, new ProductSku(), 60, TimeUnit.SECONDS);
                    }
                } finally {
                    // 解锁
                    String delLua = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
                    DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
                    redisScript.setScriptText(delLua);
                    redisScript.setResultType(Long.class);
                    stringRedisTemplate.execute(redisScript, Arrays.asList(lockKey), dflag);
                }
            } else {
                // 暂时没取到锁，在这自旋
                TUtil.sleep(100);
                return getProductSku(skuId);
            }
        }
        return productSku;
    }

    private ProductSku getProductSkuFromDB(Long skuId) {
        // 当缓存没有加载时，访问数据库 mysql
        ProductSku productSku = productSkuMapper.selectById(skuId);
        return productSku;
    }


    @Override
    public Product getProduct(Long skuId) {
        ProductSku productSku = productSkuMapper.selectById(skuId);
        Long productId = productSku.getProductId();
        Product product = productMapper.selectById(productId);
        return product;
    }

    @Override
    public SkuPrice getSkuPrice(Long skuId) {
        ProductSku productSku = productSkuMapper.selectById(skuId);
        SkuPrice skuPrice = new SkuPrice();
        BeanUtils.copyProperties(productSku, skuPrice);

        return skuPrice;
    }

    @GuiguCache(prefix = "getProductDetails")
    @Override
    public ProductDetails getProductDetails(Long skuId) {
        ProductSku productSku = productSkuMapper.selectById(skuId);
        Long productId = productSku.getProductId();
        Product product = productMapper.selectById(productId);
        ProductDetails productDetails = productDetailsMapper.selectOne(new LambdaQueryWrapper<ProductDetails>().eq(ProductDetails::getProductId, product.getId()));
        return productDetails;
    }

    @Override
    public SkuStockVo getSkuStockVo(Long skuId) {
        ProductSku productSku = productSkuMapper.selectById(skuId);
        Long id = productSku.getId();
        SkuStock skuStock = skuStockMapper.selectOne(new LambdaQueryWrapper<SkuStock>().eq(SkuStock::getSkuId, id));
        SkuStockVo skuStockVo = new SkuStockVo();
        BeanUtils.copyProperties(skuStock, skuStockVo);
        return skuStockVo;
    }

    @Override
    public List<ProductSku> ProductSkuList(Long id) {
        //  传进来的是 product 的 id
        List<ProductSku> productSkuList = productSkuMapper.selectList(new LambdaQueryWrapper<ProductSku>().eq(ProductSku::getProductId, id));

        return productSkuList;
    }

    @Override
    public List<SkuPrice> getSkuPriceList(List<Long> skuIds) {
        // 怎么通过 skuId 集合，查出 skuPrice 集合 看表写代码
        List<ProductSku> productSkuList = productSkuMapper.selectBatchIds(skuIds);
        List<SkuPrice> skuPriceList = productSkuList.stream().map(sku -> {
            SkuPrice skuPrice = new SkuPrice();
            skuPrice.setSkuId(sku.getId());
            skuPrice.setSalePrice(sku.getSalePrice());
            skuPrice.setMarketPrice(sku.getMarketPrice());
            return skuPrice;
        }).collect(Collectors.toList());
        return skuPriceList;
    }

    @Transactional
    @Override
    public String checkAndLock(String tradeNo, List<SkuLockVo> skuLockVoList) {

        // 检查分布式锁，排除重复提交的请求
        String key = "sku:checkAndLock:" + tradeNo;
        ; // 分布式锁的
        String lockKey = "sku:lock:data:" + tradeNo; // 存数据的
        String dflag = UUID.randomUUID().toString();// 删锁标识位
        boolean isCheckLock = redisTemplate.opsForValue().setIfAbsent(key, dflag, 1, TimeUnit.HOURS);

        try {
            //  Assert.isTrue(isCheckLock,"重复提交订单");
            if (!isCheckLock) {
                return "重复提交订单";
            }
            // 检查库存
      /*  for (SkuLockVo skuLockvo:skuLockVoList) {
            Long skuId = skuLockvo.getSkuId();
            Integer skuNum = skuLockvo.getSkuNum();
            SkuStock skuStock = skuStockMapper.selectById(skuId);
            Integer availableNum = skuStock.getAvailableNum();
            if(skuNum>availableNum){
                return “库存不足”;
            }
        }*/
            // 1.获得所有的 skuId
            List<Long> skuIds = skuLockVoList.stream().map(SkuLockVo::getSkuId).collect(Collectors.toList());
            // 2.获得所有的 skuStock 这里自己要学会用 in
            List<SkuStock> skuStocks = skuStockMapper.selectList(new LambdaQueryWrapper<SkuStock>().in(SkuStock::getSkuId, skuIds));
            // 3. 获得一个 skuId 配 数量的 map
            Map<Long, Integer> stockMap = skuStocks.stream().collect(Collectors.toMap(SkuStock::getSkuId, SkuStock::getAvailableNum));

            for (SkuLockVo skuLockvo : skuLockVoList) {
                Integer mySkuNum = skuLockvo.getSkuNum();
                Integer availableNum = stockMap.get(skuLockvo.getSkuId());
                if (mySkuNum > availableNum) {
                    return "库存不足";
                }
            }
            // 锁定库存
            for (SkuLockVo skuLockvo : skuLockVoList) {
//                Long skuId = skuLockvo.getSkuId();
//                Integer skuNum = skuLockvo.getSkuNum();
                skuStockMapper.lock(skuLockvo);
            }
            // 为后续释放库存，将 skuLockVoList 保存到 redis 中
            redisTemplate.opsForValue().set(lockKey, JSON.toJSONString(skuLockVoList), 24, TimeUnit.HOURS);
        } finally {
            String delLua = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
            DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
            redisScript.setScriptText(delLua);
            redisScript.setResultType(Long.class);
            redisTemplate.execute(redisScript, Arrays.asList(key), dflag);
        }
        return "";
    }

    @Override
    public void unlock(String orderNo) {

        String key = "sku:unLock:" + orderNo;// 分布式锁的
        String lockKey = "sku:lock:data:" + orderNo; // 取数据的
        String dflag = UUID.randomUUID().toString();// 删锁标识位
        boolean isCheckLock = redisTemplate.opsForValue().setIfAbsent(key, dflag, 3, TimeUnit.SECONDS);
        Assert.isTrue(isCheckLock,"订单已经取消");

        String  skuLockVoListStr  = (String) redisTemplate.opsForValue().get(lockKey);
        //SkuLockVo skuLockVo = JSON.parseObject(skuLockVoListStr, SkuLockVo.class);
        List<SkuLockVo> skuLockVoList = JSON.parseArray(skuLockVoListStr, SkuLockVo.class);

        try{
            // 拿到解锁后,释放库存
            for (SkuLockVo skuLockvo : skuLockVoList) {
//                Long skuId = skuLockvo.getSkuId();
//                Integer skuNum = skuLockvo.getSkuNum();
                skuStockMapper.unlock(skuLockvo);
            }
            redisTemplate.delete(lockKey);
        }finally {
            String delLua = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
            DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
            redisScript.setScriptText(delLua);
            redisScript.setResultType(Long.class);
            stringRedisTemplate.execute(redisScript, Arrays.asList(key), dflag);
        }
    }

    @Override
    public void minus(String orderNo) {

        String key = "sku:minusLock:" + orderNo;// 分布式锁的
        String lockKey = "sku:lock:data:" + orderNo; // 取数据的
        String dflag = UUID.randomUUID().toString();// 删锁标识位
        boolean isCheckLock = redisTemplate.opsForValue().setIfAbsent(key, dflag, 3, TimeUnit.SECONDS);
        Assert.isTrue(isCheckLock,"订单已经扣减");

        String  skuLockVoListStr  = (String) redisTemplate.opsForValue().get(lockKey);
        //SkuLockVo skuLockVo = JSON.parseObject(skuLockVoListStr, SkuLockVo.class);
        List<SkuLockVo> skuLockVoList = JSON.parseArray(skuLockVoListStr, SkuLockVo.class);

        try{
            // 拿到解锁后,释放库存
            for (SkuLockVo skuLockvo : skuLockVoList) {
//                Long skuId = skuLockvo.getSkuId();
//                Integer skuNum = skuLockvo.getSkuNum();
                skuStockMapper.minus(skuLockvo);
            }
            redisTemplate.delete(lockKey);
        }finally {
            String delLua = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
            DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
            redisScript.setScriptText(delLua);
            redisScript.setResultType(Long.class);
            stringRedisTemplate.execute(redisScript, Arrays.asList(key), dflag);
        }


    }
}

